﻿using System.Collections.Generic;
using Engine.Collections;

namespace Engine.AI.Astar
{
    public class AStarComponent
    {
        #region Constructors

        public AStarComponent()
        {
            IsPathComputed = true;
        }
        
        #endregion

        #region Fields

        private SortedList<State> _openList;
        private List<State> _closedList;
        private readonly HashSet<State> _visitedStates = new HashSet<State>();
        readonly private List<State> _path = new List<State>();
        private int _IterationNumber;
        private State _goalState;
        
        #endregion    

        #region Properties

        public IList<State> Path
        {
            get { return _path; }
        }

        #endregion
        
        #region Public Methods

        public void Cancel()
        {
            _openList  = null;
            _closedList = null;
            _path.Clear();
            IsPathComputed = true;
        }
        public void Traverse(State startState, State goalState)
        {
            _goalState = goalState;
            _visitedStates.Clear();
            startState.ComputeWeight();

            _openList = new SortedList<State>(new StateComparer());
            _closedList = new List<State>();

            _openList.Add(startState);
            IsPathComputed = false;
            _IterationNumber = 0;
        }
        
        #endregion

        #region Properties

        public bool IsPathComputed { get; set; }

        #endregion

        #region Public

        public void Update()
        {
            if (IsPathComputed) return;

            const int iterationsNumberInSingleUpdate = 1;
            _IterationNumber += iterationsNumberInSingleUpdate;            
            const int maxIterationNumber = 300;

            if (_IterationNumber > maxIterationNumber)
            {
                IsPathComputed = true;
            }
            CalcPaths(iterationsNumberInSingleUpdate);
        }

        #endregion     
       
        #region Private methods

        private void CalcPaths(int iterationsNumberInSingleUpdate)
        {
            for (int i = 0; i < iterationsNumberInSingleUpdate; i++)
            {
                State parentState = _openList[0];
                _closedList.Add(parentState);

                if (parentState.IsGoal(_goalState))
                {
                    IsPathComputed = true;

                    _path.Clear();
                    State child = _closedList[_closedList.Count - 1];
                    if (child.IsGoal(_goalState) == false)
                        return;

                    while (child != null)
                    {
                        _path.Insert(0, child);
                        child = child.Parent;
                    }                                  
                    return;
                }
                _openList.RemoveAt(0);

                _openList.AddRange(parentState.GenerateChilds(_visitedStates));
                if (_openList.Count == 0)
                {
                    IsPathComputed = true;
                    return;
                }
            }
        }

        #endregion
    }
}
